package gohandler

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/timandy/routine"
	"go-micro-framework/go_service/core/gomodel"
	"io"
	"reflect"
	"strings"
	"sync"
	"time"
)

// 1.接口系统配置参数缓存,用于接口白名单
var urlSysRemoteCache sync.Map

// 2.每个接口都对应一个规范请求缓存类,方便业务验证
var httpRequestRemoteCache sync.Map

// 3.缓存接口请求接收参数对象，通过url对应，可以一对多，
var httpRequestEntityCache sync.Map

// 4.每一次请求的行为的信息的缓存,当时线程有效
var actionRequestRemoteCache = routine.NewInheritableThreadLocal()

/**
缓存使用介绍说明：
  1.urlSysRemoteCache 服务启动时，个别特殊性的接口设置为白名单类型gomodel.HttpRequestRemote，缓存到内存中，在业务逻辑需要时，做验证判断
    -- 1.AddUrlSysWhiteRemoteCache 方法，为添加到 urlSysRemoteCache缓存中
    -- 2. GetUrlSysWhiteRemoteCache 方法，从内存缓存中查找接口是否设置系统级别对象，方便后面业务解耦实现，提供给2.使用
  2.httpRequestRemoteCache 为每个接口的通用信息存在内存中，在业务逻辑需要时，做验证判断
    -- 1.initHttpRequestRemoteCache 方法，结合 urlSysRemoteCache 系统配置，为每个接口生成一个属于这个接口的配置信息对象，并缓存在内存中
    -- 2. GetHttpRequestRemoteCache 方法，从内存缓存中查找每个接口代理对象，方便后面业务解耦实现，提供给3.使用
3.httpRequestEntityCache 服务启动时，对应每个请求接口--1-1对象一个参数接入请求对象缓存到内存中，在业务逻辑需要时，解耦和逻辑处理
    -- 1.AddUrlHttpRequestEntityCache 方法，结合 httpRequestRemoteCache 接口基本配置，为每个接口当前线程内的行为请求对象信息，并缓存在内存中
    -- 2.GetUrlHttpRequestEntityCache 方法，从内存缓存中查找这个接口，接入请求参数接收天象，方便后面业务解耦实现，用于md5防篡改和值是否符合要求
 4.actionRequestRemoteCache 为每个接口的当前线程安全，只在这一次请求中有效的参数请求对象
    -- 1.InitActionHttpRequest 方法，结合 httpRequestRemoteCache 接口基本配置，为每个接口当前线程内的行为请求对象信息，并缓存在内存中
    -- 2. GetActionHttpRequest 方法，从内存缓存中查找这个接口当前线程请求的代理对象信息，方便后面业务解耦实现，提供给业务方使用
    -- 3. RemoveActionHttpRequest 删除当前线程请求的缓存对象信息
*/
//1--1.AddUrlSysWhiteRemoteCache 方法，为添加到 urlSysRemoteCache缓存中 Cache 缓存实体对象所有的字段名
func AddUrlSysWhiteRemoteCache(remotes ...*gomodel.UrlRemote) {
	for _, remote := range remotes {
		urlSysRemoteCache.Store(remote.Url, remote)
	}
}

// 1-- 2. GetUrlSysWhiteRemoteCache 方法，从内存缓存中查找接口是否设置系统级别对象，方便后面业务解耦实现，提供给2.使用
func GetUrlSysWhiteRemoteCache(url string) (*gomodel.HttpRequestRemote, bool) {
	//不存在,获取项目启动配置信息,初始化到cache中,再返回
	config, configOk := urlSysRemoteCache.Load(url)
	m, isReal := config.(*gomodel.UrlRemote)
	request := gomodel.NewHttpRequestRemote()
	if configOk && isReal {
		request.SetUrlRemote(*m)
	} else {
		m2 := gomodel.NewUrlRemote()
		m2.Url = url
		request.SetUrlRemote(*m2)
	}
	return request, true
}

// 2.httpRequestRemoteCache 为每个接口的通用信息存在内存中，在业务逻辑需要时，做验证判断
// 2-- 1.initHttpRequestRemoteCache 方法，结合 urlSysRemoteCache 系统配置，为每个接口生成一个属于这个接口的配置信息对象，并缓存在内存中
// 接口自定义为公共参数为header头获取
func initHttpRequestRemoteCache(ctx *gin.Context) *gomodel.HttpRequestRemote {
	var url = ctx.FullPath()
	//存在返回
	if httpRequestRemote, ok := GetHttpRequestRemoteCache(url); ok {
		return httpRequestRemote
	}
	//不存在,获取项目启动配置信息,初始化到cache中,再返回
	requestRemote, _ := GetUrlSysWhiteRemoteCache(url)

	//固定的
	requestRemote.Url = url
	requestRemote.IsPost = strings.EqualFold("POST", ctx.Request.Method)

	//接口自定义为json接口
	requestRemote.IsJsonRequest = strings.EqualFold("true", ctx.GetHeader("Content-Json"))
	if requestRemote.IsJsonRequest || requestRemote.IsJson {
		requestRemote.IsJsonRequest = true
		requestRemote.IsJson = true
	}
	//接口自定义为公共参数为header头获取
	httpRequestRemoteCache.Store(url, requestRemote)
	return requestRemote
}

// 2.httpRequestRemoteCache 为每个接口的通用信息存在内存中，在业务逻辑需要时，做验证判断
// 2-- 2. GetHttpRequestRemoteCache 方法，从内存缓存中查找每个接口代理对象，方便后面业务解耦实现，提供给3.使用
// Cache 每个接口都对应一个规范请求缓存类,方便业务验证 缓存实体对象所有的字段名
func GetHttpRequestRemoteCache(url string) (*gomodel.HttpRequestRemote, bool) {
	if remoteCache, ok := httpRequestRemoteCache.Load(url); ok {
		r, isReal := remoteCache.(*gomodel.HttpRequestRemote)
		if isReal {
			return r, true
		}
	}
	return nil, false
}

// 3.httpRequestEntityCache 服务启动时，对应每个请求接口--1-1对象一个参数接入请求对象缓存到内存中，在业务逻辑需要时，解耦和逻辑处理
// 3-- 1.AddUrlHttpRequestEntityCache 方法，结合 httpRequestRemoteCache 接口基本配置，为每个接口当前线程内的行为请求对象信息，并缓存在内存中
// 通过请接接口唯一url，获取参数接收对象，用于参数是否符合规定要求检验,requestEntity any 可以是数组对象
func AddUrlHttpRequestEntityCache(url string, requestEntity any) bool { //(*gomodel.ErrorCodeEnum, string) {
	//当存在时，返回false
	if requestEntity == nil {
		return false
	}
	if _, ok := httpRequestEntityCache.Load(url); ok {
		return false
	}
	httpRequestEntityCache.Store(url, requestEntity)
	return true
}

// 3.httpRequestEntityCache 服务启动时，对应每个请求接口--1-1对象一个参数接入请求对象缓存到内存中，在业务逻辑需要时，解耦和逻辑处理
// 3-- 2.GetUrlHttpRequestEntityCache 方法，从内存缓存中查找这个接口，接入请求参数接收天象，方便后面业务解耦实现，用于md5防篡改和值是否符合要求
// 通过请接接口唯一url，获取参数接收对象，用于参数是否符合规定要求检验
func GetUrlHttpRequestEntityCache(url string) (any, bool) { //(*gomodel.ErrorCodeEnum, string) {
	model, ok := httpRequestEntityCache.Load(url)
	return model, ok
	//
}

// 4.actionRequestRemoteCache 为每个接口的当前线程安全，只在这一次请求中有效的参数请求对象
// 4-- 1.InitActionHttpRequest 方法，结合 httpRequestRemoteCache 接口基本配置，为每个接口当前线程内的行为请求对象信息，并缓存在内存中
// 为接口的每一次请求，初始化一个实现实例，记得当前用户的行为数据
func InitActionHttpRequest(cxt *gin.Context) (*gomodel.ErrorCodeEnum, error) {
	//var url = cxt.FullPath()
	var sysTime = time.Now().Unix()
	var request = initHttpRequestRemoteCache(cxt)
	var paramPost gomodel.HttpRequestPost
	//验证每个请求,获取公共参数的方法
	request.IsHeader = strings.EqualFold("true", cxt.GetHeader("isHeader"))
	//1.是否从头部获取公共参数设置
	if request.IsHeader || request.IsJsonRequest {
		// 使用ShouldBindHeader方法将Header参数映射到User对象
		var headerPost gomodel.HttpRequestHeaderPost

		if err := cxt.ShouldBindHeader(&headerPost); err != nil {
			//cxt.JSON(http.StatusOK, response)
			return gomodel.SYS_HEADER_PARAM_FAIL, err
		}
		paramPost = gomodel.CoverHttpRequestPost(headerPost)
	} else if err := cxt.ShouldBind(&paramPost); err != nil {
		return gomodel.SYS_PARAM_ERROR, err

	}

	var action = gomodel.NewActionRequestRemote()
	action.SetUrlRemote(request.UrlRemote)
	action.SetPost(paramPost)
	//request
	action.IsJsonRequest = request.IsJsonRequest
	action.IsPost = request.IsPost
	action.IsHeader = request.IsHeader
	action.ClientIp = cxt.ClientIP()
	action.ReceivedTime = sysTime
	action.NetTime = sysTime - paramPost.Times
	//action.ClientMd5Sign = paramPost.CliSign
	//SrvMd5Sign    string //服务验证用户请求url加密内容
	actionRequestRemoteCache.Set(action)
	return nil, nil
}

// 4.actionRequestRemoteCache 为每个接口的当前线程安全，只在这一次请求中有效的参数请求对象
// 4-- 2. GetActionHttpRequest 方法，从内存缓存中查找这个接口当前线程请求的代理对象，方便后面业务解耦实现，提供给业务方使用
func GetActionHttpRequest(url string) *gomodel.ActionHttpRequestRemote {
	var action = actionRequestRemoteCache.Get()
	if action != nil {
		r, isReal := action.(*gomodel.ActionHttpRequestRemote)
		if isReal {
			return r
		}
	}
	return nil
}

// 删除当前线程缓存信息
func RemoveActionHttpRequest() {
	actionRequestRemoteCache.Remove()
}

func GetActionHttpRequestJson(cxt *gin.Context) (any, *gomodel.ErrorCodeEnum) {
	var url = cxt.FullPath()
	model, ok := GetUrlHttpRequestEntityCache(url)
	//说明没有接收对象，
	if model == nil || !ok {
		return nil, gomodel.SYS_PARAM_CHECK_ENTITY
	}
	var actionHttpRequest = GetActionHttpRequest(url)
	if actionHttpRequest != nil && actionHttpRequest.GetJsonBody() != nil {
		entity := actionHttpRequest.GetJsonBody()
		if reflect.TypeOf(model) == reflect.TypeOf(entity) {
			return entity, gomodel.SYS_SUCCESS
		}

	}
	body, err := io.ReadAll(cxt.Request.Body)
	if err != nil || body == nil {
		return nil, gomodel.SYS_PARAM_JSON_FAIL
	}
	if err := json.Unmarshal(body, model); err != nil {
		return nil, gomodel.SYS_PARAM_JSON_FAIL
	}
	actionHttpRequest.SetJsonBody(model)
	actionRequestRemoteCache.Set(actionHttpRequest)
	return model, gomodel.SYS_SUCCESS

}

func ActionContextCache() {
	// 创建父Context
	parentCtx := context.Background()
	//threadID := runtime.GetCu //.GetCurrentThreadID()
	// 创建带有数据的子Context
	ctx := context.WithValue(parentCtx, "key", "value")
	//context.WithTimeout(parentCtx)
	var wg sync.WaitGroup
	wg.Add(1)
	// 在子goroutine中获取数据
	go func(ctx context.Context) {
		defer wg.Done()
		value := ctx.Value("key")
		fmt.Println("子goroutine中获取到的数据:", value)
	}(ctx)
	wg.Wait()
}

// GetModel 获取
func GetRemote(url string) *gomodel.HttpRequestRemote {
	if model, ok := httpRequestRemoteCache.Load(url); ok {
		m, isReal := model.(*gomodel.HttpRequestRemote)
		if isReal {
			return m
		}
	}
	return nil
}
